package cn.xiaolang.function.file_manage.io;

import cn.xiaolang.function.file_manage.io.exception.FileIncompleteException;
import cn.xiaolang.function.file_manage.io.exception.FileNotExistException;
import cn.xiaolang.function.file_manage.io.exception.FileNotMatchException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 文件工具类
 *
 * @author Dean
 * @date 2022-01-04
 */
@Slf4j
public class FileHelper {

    private static final String statisticFileName = "statistic.part";
    private static final Pattern fileClipPattern = Pattern.compile("part.+(.part)$");


    public static void mergeFile(File srcDir, File tarFile) throws IOException {
        FileOutputStream fos = validateFile(srcDir, tarFile);
        // file clips
        File[] clipFiles = srcDir.listFiles(file -> !file.getName().equalsIgnoreCase(statisticFileName)
                && fileClipPattern.matcher(file.getName()).find());
        if (Objects.isNull(clipFiles)) {
            throw new FileIncompleteException();
        }
        for (File file : clipFiles) {
            if (!file.getName().equalsIgnoreCase(statisticFileName)) {
                FileInputStream fis = new FileInputStream(file);
                byte[] buf = new byte[1024];
                int len = 0;
                while ((len = fis.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                }
                fis.close();
            }
        }
        fos.close();
    }

    private static FileOutputStream validateFile(File srcDir, File tarFile) throws FileNotFoundException {
        if (!srcDir.exists()) {
            throw new FileNotExistException();
        }
        File[] dirFiles = srcDir.listFiles();
        if (!srcDir.isDirectory() || Objects.isNull(dirFiles)) {
            throw new FileNotMatchException(srcDir + "is not directory");
        }
        FileOutputStream fos = new FileOutputStream(tarFile);
        boolean flag = false;
        for (File file2 : dirFiles) {
            if (file2.getName().equalsIgnoreCase(statisticFileName)) {
                flag = true;
                break;
            }
        }
        if (!flag) {
            throw new FileIncompleteException();
        }
        return fos;
    }

    /**
     * 使用序列流（是一种功能流），把多个输入流合并成一个输入流，来合并文件
     *
     * @param srcDir source file
     * @param tarFile target file
     * @throws IOException IOException
     */
    public static void mergeFileBySeq(File srcDir, File tarFile) throws IOException {
        FileOutputStream fos = validateFile(srcDir, tarFile);
        // file clips
        File[] clipFiles = srcDir.listFiles(file -> !file.getName().equalsIgnoreCase(statisticFileName)
                && fileClipPattern.matcher(file.getName()).find());
        if (Objects.isNull(clipFiles)) {
            throw new FileIncompleteException("miss file");
        }
        // 创建一个list集合
        List<InputStream> fileInStreams = new LinkedList<>();
        for (File file : clipFiles) {
            fileInStreams.add(new FileInputStream(file));
        }
        Enumeration<InputStream> en = Collections.enumeration(fileInStreams);
        // 把转换后的Enumeration对象，传给序列化对象
        SequenceInputStream sis = new SequenceInputStream(en);
        transfer(sis, fos);
    }


    public static void transfer(InputStream fis, OutputStream fos) {
        try {
            transferNotClose(fis, fos);
        } finally {
            try {
                fis.close();
                fos.close();
            } catch (IOException e) {
                log.error("close stream :", e);
            }
        }
    }

    private static void transferNotClose(InputStream fis, OutputStream fos) {
        transferNotClose(fis, fos, 1024);
    }

    private static void transferNotClose(InputStream fis, OutputStream fos, Integer byteSize) {
        try {
            byte[] buf = new byte[Objects.isNull(byteSize) ? 1024 : byteSize];
            int len = 0;
            while ((len = fis.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
        } catch (IOException e) {
            log.error("", e);
        }
    }

    /**
     * 对文件进行切割
     *
     * @param srcFile source file
     * @param tarFile target file
     * @throws IOException IOException
     */
    public static void splitFile(File srcFile, File tarFile) throws IOException {
        splitFile(srcFile, tarFile, 104857600);
    }

    /**
     * 对文件进行切割
     *
     * @param srcFile source file
     * @param tarFile target file
     * @param readBatchSize default:100M
     * @throws IOException IOException
     */
    public static void splitFile(File srcFile, File tarFile, Integer readBatchSize) throws IOException {
        // 首先判断，目的路径是否存在
        if (!srcFile.exists()) {
            throw new FileNotExistException("file not found");
        }
        // 再判断目的文件对象的路径是否存在,不存在的话，创建一个目录文件
        if (!tarFile.exists() && !tarFile.mkdirs()) {
            return;
        }
        // 创建一个字节输入流对象，用来读取源文件的路径，传递src
        FileInputStream fis = new FileInputStream(srcFile);
        byte[] buf = new byte[readBatchSize];
        int partSerial = 1;
        int len = 0;
        while ((len = fis.read(buf)) != -1) {
            // 创建一个字节输出流对象，向目的路径文件写入文件，并传递一个新建文件对象，用来存储读取的一部分文件
            FileOutputStream fos = new FileOutputStream(new File(tarFile, "part" + partSerial + ".part"));
            // 向文件中写入数据
            fos.write(buf, 0, len);
            // 关闭输出流
            fos.close();
            partSerial++;
        }
        // 关闭输入流
        fis.close();
        // 创建一个输入流，传递一个记录文件
        FileOutputStream fos = new FileOutputStream(new File(tarFile, statisticFileName));
        fos.write(("total file size:" + (partSerial - 1)).getBytes());
        fos.close();
    }


    /**
     * file copy by bytes
     *
     * @param srcFile source file
     * @param tarFile target file
     * @param streamEnum FileCopyByteEnum
     * @throws IOException IOException
     */
    public static void fileCopyByte(File srcFile, File tarFile, FileCopyByteEnum streamEnum) throws IOException {
        if (!tarFile.exists() && !tarFile.createNewFile()) {
            return;
        }
        try (FileInputStream inputStream = new FileInputStream(srcFile);
             FileOutputStream outputStream = new FileOutputStream(tarFile);) {
            streamEnum.transfer(inputStream, outputStream);
        }
    }

    /**
     * file copy by char
     *
     * @param srcFile source file
     * @param tarFile target file
     * @param bufferedEnum FileCopyCharEnum
     * @throws IOException IOException
     */
    public static void fileCopyChar(File srcFile, File tarFile, FileCopyCharEnum bufferedEnum) throws IOException {
        if (!tarFile.exists() && !tarFile.createNewFile()) {
            return;
        }
        try (FileReader reader = new FileReader(srcFile);
             FileWriter writer = new FileWriter(tarFile, true)) {
            bufferedEnum.transfer(reader, writer);
        }
    }

    /**
     * @param dirPath the path of directory
     * @return the files name
     */
    public static List<String> getAllFileFromDir(String dirPath) {
        File dir = new File(dirPath);
        File[] files = dir.listFiles();
        if (Objects.isNull(files) || files.length <= 0) {
            return Collections.emptyList();
        }
        List<String> fileList = new ArrayList<>();
        for (File file : files) {
            if (file.isDirectory()) {
                // 递归调用
                fileList.addAll(getAllFileFromDir(file.getAbsolutePath()));
            } else {
                fileList.add(file.getAbsolutePath());
            }
        }
        return fileList;
    }


    public static String sizeByteFormat(String size) {
        int iPos = 0;
        StringBuilder sBuf = new StringBuilder();
        try {
            if (size.trim().compareTo("") == 0) {
                return "";
            }
            double i = Double.parseDouble(size);
            if (i <= 0) {
                sBuf.append("");
            } else if (i < 1024) {
                sBuf.append(i).append(" B");
                iPos = sBuf.lastIndexOf(".00 B");
                if (iPos > 0) {
                    sBuf.delete(iPos, sBuf.length() - 2);
                }
            } else if (i < 1024 * 1024) {
                sBuf.append(new java.text.DecimalFormat(".00").format(i / 1024)).append(" KB");    //四舍五入
                iPos = sBuf.lastIndexOf(".00 KB");
                if (iPos > 0) {
                    sBuf.delete(iPos, sBuf.length() - 3);
                }
            } else if (i < 1024 * 1024 * 1024) {
                sBuf.append(new java.text.DecimalFormat(".00").format(i / (1024 * 1024))).append(" M");    //四舍五入
                iPos = sBuf.lastIndexOf(".00 M");
                if (iPos > 0) {
                    sBuf.delete(iPos, sBuf.length() - 2);
                }
            } else {
                sBuf.append(new java.text.DecimalFormat(".00").format(i / (1024 * 1024 * 1024))).append(" G");    //四舍五入
                iPos = sBuf.lastIndexOf(".00 G");
                if (iPos > 0) {
                    sBuf.delete(iPos, sBuf.length() - 2);
                }
            }
        } catch (Exception e) {
            return "";
        }
        return sBuf.toString();
    }
}
